//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "gprocess.h"
#include "cservice.h"
#include <aura.h>

CGhostProcess::~CGhostProcess()
{
    if (NULL != m_pName) {
        WStringBuf::Free(m_pName);
        m_pName = NULL;
    }
    if (NULL != m_pArgs) {
        WStringBuf::Free(m_pArgs);
        m_pArgs = NULL;
    }
    if (NULL != m_hExitEvent) {
        AuraEvent_Destroy(m_hExitEvent);
        m_hExitEvent = NULL;
    }
    if (NULL != m_hExitLock) {
        AuraCriticalSection_Destroy(m_hExitLock);
        m_hExitLock = NULL;
    }
    if (NULL != m_hProcess) {
        AuraProcess_Destroy(m_hProcess);
        m_hProcess = NULL;
    }
}

ECode CGhostProcess::Initialize0(
    /* [in] */ WString name,
    /* [in] */ WString args)
{
    ECode ec = NOERROR;
    Int32 length = 0;

    assert(!name.IsNullOrEmpty());

    AuraSystem_GetTime((Aura_SystemTime *)&m_startTime);

    length = name.GetLength(AURA_MAX_PATH) + 1;
    m_pName = WStringBuf::Alloc(length);
    if (NULL == m_pName) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }
    m_pName->Copy(name);

    if (!args.IsNull()) {
        length = args.GetLength(AURA_MAX_PATH) + 1;
        m_pArgs = WStringBuf::Alloc(length);
        if (NULL == m_pArgs) {
            ec = E_OUT_OF_MEMORY;
            goto ErrorExit;
        }
        m_pArgs->Copy(args);
    }
    else {
        m_args.Copy(L"");
    }

    ec = AuraEvent_Create(AURA_TRUE, AURA_UNSIGNALED, &m_hExitEvent);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = AuraCriticalSection_Create(&m_hExitLock);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    return ec;

ErrorExit:
    if (NULL != m_hExitEvent) {
        AuraEvent_Destroy(m_hExitEvent);
        m_hExitEvent = NULL;
    }
    if (NULL != m_pName) {
        WStringBuf::Free(m_pName);
        m_pName = NULL;
    }
    if (NULL != m_pArgs) {
        WStringBuf::Free(m_pArgs);
        m_pArgs = NULL;
    }
    return ec;
}

UInt32 CGhostProcess::AddRef()
{
    Int32 nRefs = m_nRefs.Increment();

    return (UInt32)nRefs;
}

UInt32 CGhostProcess::Release()
{
    Int32 nRefs;

    nRefs = m_nRefs.Decrement();

    if (0 == nRefs) {
        CGhostProcess::S_DeleteFromHashList(this);
        m_inList.Initialize();
        delete this;
    }
    return (UInt32)nRefs;
}

ECode CGhostProcess::Kill(
    /* [in] */ ExitCode exitCode)
{
    Lock(m_hExitLock);
    if (!m_bAlive) {
        Unlock(m_hExitLock);
        return NOERROR;
    }

    AuraProcess_Kill(m_hProcess, (Aura_ExitCode)exitCode);
    CGhostProcess::S_DeleteFromActiveList(this);
    m_exitCode  = exitCode;
    AuraSystem_GetTime((Aura_SystemTime *)&m_exitTime);
    m_bAlive = FALSE;

    Unlock(m_hExitLock);

    return (ECode)AuraEvent_Signal(m_hExitEvent, AURA_SIGNALED);
}

ECode CGhostProcess::GetStartInfo(
    /* [out] */ WStringBuf ** ppName,
    /* [out] */ WStringBuf ** ppArgs)
{
    assert(NULL != ppName);
    assert(NULL != ppArgs);
    assert(NULL != m_pName);

    *ppName = m_pName;
    if (NULL != m_pArgs) {
        *ppArgs = m_pArgs;
    }
    else {
        *ppArgs = &m_args;
    }

    return NOERROR;
}

ECode CGhostProcess::GetExitCode(
    /* [out] */ ExitCode * pExitCode)
{
    assert(NULL != pExitCode);

    if (m_bAlive) {
        return E_PROCESS_NOT_EXITED;
    }
    *pExitCode = m_exitCode;

    return NOERROR;
}

ECode CGhostProcess::GetStartTime(
    /* [out] */ SystemTime * pTime)
{
    assert(NULL != pTime);

    *pTime = m_startTime;
    return NOERROR;
}

ECode CGhostProcess::GetExitTime(
    /* [out] */ SystemTime * pTime)
{
    assert(NULL != pTime);

    if (m_bAlive) {
        return E_PROCESS_NOT_EXITED;
    }
    *pTime = m_exitTime;

    return NOERROR;
}

ECode CGhostProcess::WaitForExit(
    /* [in]  */ Millisecond32 ms,
    /* [out] */ WaitResult * pResult)
{
    assert(NULL != pResult);

    if (!m_bAlive) {
        *pResult = WaitResult_OK;
        return NOERROR;
    }

    return (ECode)AuraEvent_Wait(m_hExitEvent, (Aura_Millisecond32)ms, (Aura_WaitResult *)pResult, AURA_NULL);
}


ECode CGhostProcess::IsAlive(
    /* [out] */ Boolean * pbAlive)
{
    assert(NULL != pbAlive);

    *pbAlive = m_bAlive;

    return NOERROR;
}

ECode CGhostProcess::GetId(
    /* [out] */ ProcessId * pProcessId)
{
    assert(NULL != pProcessId);
    *pProcessId = m_processId;

    return NOERROR;
}

ECode CGhostProcess::GetProcessorTimes(
        /* [out] */ Millisecond32 * pUserTime,
        /* [out] */ Millisecond32 * pPrivilegedTime)
{
    assert(NULL != pUserTime);
    assert(NULL != pPrivilegedTime);
    assert(!m_bAlive);

    *pUserTime = m_userTime;
    *pPrivilegedTime = m_privilegedTime;

    return NOERROR;
}

ECode CGhostProcess::DoAtExit(
        /* [in] */ ExitCode exitCode,
        /* [in] */ Millisecond32 userTime,
        /* [in] */ Millisecond32 privilegedTime)
{
    Lock(m_hExitLock);
    if (!m_bAlive) {
        Unlock(m_hExitLock);
        return NOERROR;
    }

    CGhostProcess::S_DeleteFromActiveList(this);
    m_exitCode  = exitCode;
    m_userTime  = userTime;
    m_privilegedTime = privilegedTime;
    AuraSystem_GetTime((Aura_SystemTime *)&m_exitTime);
    m_bAlive = FALSE;

    Unlock(m_hExitLock);

    return (ECode)AuraEvent_Signal(m_hExitEvent, AURA_SIGNALED);
}

ECode CGhostProcess::DoAtExceptionalExit(
        /* [out] */ Boolean * pbAliveBefore)
{
    assert(NULL != pbAliveBefore);

    Lock(m_hExitLock);
    if (!m_bAlive) {
        *pbAliveBefore = FALSE;
        Unlock(m_hExitLock);
        return NOERROR;
    }

    *pbAliveBefore = TRUE;
    CGhostProcess::S_DeleteFromActiveList(this);
    AuraProcess_GetExitCode(m_hProcess, (Aura_ExitCode *)&m_exitCode);
    AuraSystem_GetTime((Aura_SystemTime *)&m_exitTime);
    m_bAlive = FALSE;
    Unlock(m_hExitLock);

    return (ECode)AuraEvent_Signal(m_hExitEvent, AURA_SIGNALED);
}

// ----------------------------------------------------
ECode CGhostProcessFactory::CreateGhost(
        /* [out] */ Ghost ** ppGhost,
        /* [out] */ Boolean * pbExisted)
{
    assert(NULL != ppGhost);
    assert(NULL == pbExisted);

    return CGhostProcess::S_Create(m_priorityRank,
                                  m_capacity,
                                  m_name, m_args,
                                  (CGhostProcess**)ppGhost);
}

ECode CGhostProcessFactory::GetGhost(
        /* [out] */ Ghost ** ppGhost)
{
    assert(NULL != ppGhost);
    return CGhostProcess::S_Find(m_processId, (CGhostProcess**)ppGhost);
}

// ------static functions -----------------------------
DLinkNode CGhostProcess::s_hashList[GHOST_HS_SLOTS];

DLinkNode CGhostProcess::s_activeList;
Int32     CGhostProcess::s_activeListState;
PVoid     CGhostProcess::s_hLock;

ECode CGhostProcess::S_Initialize()
{
    s_activeList.Initialize();
    s_activeListState = 0;

    for (Int32 i = 0; i < GHOST_HS_SLOTS; i++) {
        s_hashList[i].Initialize();
    }
    return (ECode)AuraCriticalSection_Create(&s_hLock);
}

void CGhostProcess::S_Destroy()
{
    if (NULL != s_hLock) {
        AuraCriticalSection_Destroy(s_hLock);
        s_hLock = NULL;
    }
    return ;
}

ECode CGhostProcess::S_Create(
    /* [in] */ WString name,
    /* [in] */ WString args,
    /* [in] */ ProcessId processId)
{

    CGhostProcess * pGhost = NULL;
    DLinkNode  * pListHead = NULL;
    Aura_Handle hProcess = NULL;
    ECode  ec = NOERROR;

    assert(!name.IsNullOrEmpty());

    pListHead = &s_hashList[GHOST_HASHCODE(processId)];
    Lock(s_hLock);
    ForEachDLinkNode(CGhostProcess*, pGhost, pListHead)
    {
        if (pGhost->IsEqual(processId)) {
            Unlock(s_hLock);
            return E_ALREADY_EXIST;
        }
    }
    pGhost = new CGhostProcess();
    if (NULL == pGhost) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }
    ec = pGhost->Initialize0(name, args);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = AuraProcess_Open((Aura_ProcessId)processId, &hProcess);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    pGhost->Initialize1(processId, hProcess);
    pGhost->AddRef();

    pListHead->InsertLast(pGhost);
    s_activeList.InsertLast(&pGhost->m_inList);
    s_activeListState++;

    Unlock(s_hLock);

    return NOERROR;

ErrorExit:
    Unlock(s_hLock);
    if (NULL != pGhost) {
        delete pGhost;
    }
    return ec;
}

ECode CGhostProcess::S_Create(
    /* [in] */ ThreadPriorityRank priorityRank, /* [UNUSED */
    /* [in] */ Int32 capacity, /* [UNUSED */
    /* [in] */ WString name,
    /* [in] */ WString args,
    /* [out]*/ CGhostProcess ** ppGhost)
{
    CGhostProcess * pGhost = NULL;
    ProcessId processId = AURA_INVALID_PID;
    Aura_Handle hProcess = NULL;
    ECode ec = NOERROR;

    assert(!name.IsNullOrEmpty());
    assert(NULL != ppGhost);

    pGhost = new CGhostProcess();
    if (NULL == pGhost) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }
    ec = pGhost->Initialize0(name, args);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    ec = (ECode)AuraProcess_Create(
                     (Aura_PWChar)(const wchar_t *)name,
                     (Aura_PWChar)(const wchar_t *)args,
                     &hProcess,
                     (Aura_ProcessId *)&processId);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    AuraProcess_SetPriorityRank(hProcess, (Aura_ThreadPriorityRank)priorityRank);

    pGhost->Initialize1(processId, hProcess);
    pGhost->AddRef();   // for the new process to ref itself.
    pGhost->AddRef();   // ref by request process.

    Lock(s_hLock);
    s_hashList[GHOST_HASHCODE(processId)].InsertLast(pGhost);
    s_activeList.InsertLast(&pGhost->m_inList);
    s_activeListState++;
    Unlock(s_hLock);

    *ppGhost = pGhost;
    return NOERROR;

ErrorExit:
    if (NULL != pGhost) {
        delete pGhost;
    }
    return ec;
}

ECode CGhostProcess::S_Find(
    /* [in] */ ProcessId processId,
    /* [out]*/ CGhostProcess ** ppGhost)
{
    CGhostProcess * pGhost = NULL;
    DLinkNode  * pHead = NULL;

    pHead = &s_hashList[GHOST_HASHCODE(processId)];
    Lock(s_hLock);
    ForEachDLinkNode(CGhostProcess*, pGhost, pHead)
    {
        if (pGhost->IsEqual(processId)) {
            pGhost->AddRef();
            Unlock(s_hLock);
            *ppGhost = pGhost;
            return NOERROR;
        }
    }
    Unlock(s_hLock);

    return E_DOES_NOT_EXIST;
}

EXTERN CServiceCentral *s_pServiceCentral;

void CGhostProcess::S_DeleteFromActiveList(
        /* [in] */ CGhostProcess * pGhost)
{
    Lock(s_hLock);
    pGhost->m_inList.Detach();
   // pGhost->m_inList.Initialize();
    s_activeListState++;
    Unlock(s_hLock);

    s_pServiceCentral->UnregisterAll(pGhost->m_processId);

    return ;
}

void CGhostProcess::S_DeleteFromHashList(
        /* [in] */ CGhostProcess * pGhost)
{
    Lock(s_hLock);
    pGhost->Detach();
    Unlock(s_hLock);

    return ;
}

